/*
 * Copyright 2014-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
 */

package io.ktor.client.webrtc

import kotlinx.coroutines.flow.*

/**
 * Represents events related to media tracks in a WebRTC peer connection.
 *
 * These events allow monitoring when tracks are added to or removed from the connection.
 */
public sealed interface TrackEvent {
    public val track: WebRtcMedia.Track

    /**
     * Event emitted when the track is added to the connection.
     */
    public class Add(override val track: WebRtcMedia.Track) : TrackEvent

    /**
     * Event emitted when the track is removed from the connection by the other peer.
     * This is different from MediaStreamTrack's `ended` event.
     */
    public class Remove(override val track: WebRtcMedia.Track) : TrackEvent
}

/**
 * Interface providing access to various events and state changes in a WebRTC peer connection.
 *
 * This interface allows observing connection states, ICE candidates, and other events
 * through Kotlin Flows, enabling reactive programming with WebRTC connections.
 */
public interface WebRtcConnectionEvents {
    /**
     * Flow of connection state changes.
     */
    public val state: StateFlow<WebRtc.ConnectionState>

    /**
     * Flow of ICE candidates generated by this peer connection.
     */
    public val iceCandidates: SharedFlow<WebRtc.IceCandidate>

    /**
     * Flow of ICE connection state changes.
     */
    public val iceConnectionState: StateFlow<WebRtc.IceConnectionState>

    /**
     * Flow of ICE gathering state changes.
     */
    public val iceGatheringState: StateFlow<WebRtc.IceGatheringState>

    /**
     * Flow of signaling state changes.
     */
    public val signalingState: StateFlow<WebRtc.SignalingState>

    /**
     * Flow of events on a media track (e.g., add, remove).
     */
    public val trackEvents: SharedFlow<TrackEvent>

    /**
     * Flow of newly added connection dataChannels,
     * as a result of the remote peer calling [WebRtcPeerConnection.createDataChannel].
     */
    public val dataChannelEvents: SharedFlow<DataChannelEvent>

    /**
     * Flow of connection statistics.
     */
    public val stats: StateFlow<List<WebRtc.Stats>>

    /**
     * Sets a callback to be invoked when negotiation (offer-answer exchange, etc.) is needed for this connection.
     */
    public val negotiationNeeded: SharedFlow<Unit>
}

/**
 * Internal implementation of [WebRtcConnectionEvents] that emits events to flows.
 *
 * This class maintains the mutable flows and provides methods to emit events
 * from platform-specific WebRTC implementations.
 */
public class WebRtcConnectionEventsEmitter(
    config: WebRtcConnectionConfig
) : WebRtcConnectionEvents {

    // Private mutable flows
    private val iceCandidatesFlow: MutableSharedFlow<WebRtc.IceCandidate> =
        MutableSharedFlow(config.iceCandidatesReplay)
    private val iceConnectionStateFlow: MutableStateFlow<WebRtc.IceConnectionState> =
        MutableStateFlow(WebRtc.IceConnectionState.NEW)
    private val connectionStateFlow: MutableStateFlow<WebRtc.ConnectionState> =
        MutableStateFlow(WebRtc.ConnectionState.NEW)
    private val iceGatheringStateFlow: MutableStateFlow<WebRtc.IceGatheringState> =
        MutableStateFlow(WebRtc.IceGatheringState.NEW)
    private val signalingStateFlow: MutableStateFlow<WebRtc.SignalingState> =
        MutableStateFlow(WebRtc.SignalingState.STABLE)
    private val trackEventsFlow: MutableSharedFlow<TrackEvent> = MutableSharedFlow(config.remoteTracksReplay)
    private val dataChannelEventsFlow: MutableSharedFlow<DataChannelEvent> =
        MutableSharedFlow(config.dataChannelEventsReplay)
    private val statsFlow: MutableStateFlow<List<WebRtc.Stats>> = MutableStateFlow(listOf())
    private val negotiationNeededFlow: MutableSharedFlow<Unit> = MutableSharedFlow(extraBufferCapacity = 1)

    // Public flows
    override val stats: StateFlow<List<WebRtc.Stats>> = statsFlow.asStateFlow()
    override val trackEvents: SharedFlow<TrackEvent> = trackEventsFlow.asSharedFlow()
    override val negotiationNeeded: SharedFlow<Unit> = negotiationNeededFlow.asSharedFlow()
    override val state: StateFlow<WebRtc.ConnectionState> = connectionStateFlow.asStateFlow()
    override val iceCandidates: SharedFlow<WebRtc.IceCandidate> = iceCandidatesFlow.asSharedFlow()
    override val signalingState: StateFlow<WebRtc.SignalingState> = signalingStateFlow.asStateFlow()
    override val dataChannelEvents: SharedFlow<DataChannelEvent> = dataChannelEventsFlow.asSharedFlow()
    override val iceGatheringState: StateFlow<WebRtc.IceGatheringState> = iceGatheringStateFlow.asStateFlow()
    override val iceConnectionState: StateFlow<WebRtc.IceConnectionState> = iceConnectionStateFlow.asStateFlow()

    // Helper functions to emit events in WebRtcConnection implementations,
    // So it doesn't have to extend CoroutineScope and run `launch`

    public fun emitIceCandidate(candidate: WebRtc.IceCandidate) {
        iceCandidatesFlow.tryEmit(candidate)
    }

    public fun emitStats(stats: List<WebRtc.Stats>) {
        statsFlow.tryEmit(stats)
    }

    public fun emitNegotiationNeeded() {
        negotiationNeededFlow.tryEmit(Unit)
    }

    public fun emitIceConnectionStateChange(state: WebRtc.IceConnectionState) {
        iceConnectionStateFlow.tryEmit(state)
    }

    public fun emitConnectionStateChange(state: WebRtc.ConnectionState) {
        connectionStateFlow.tryEmit(state)
    }

    public fun emitIceGatheringStateChange(state: WebRtc.IceGatheringState) {
        iceGatheringStateFlow.tryEmit(state)
    }

    public fun emitSignalingStateChange(state: WebRtc.SignalingState) {
        signalingStateFlow.tryEmit(state)
    }

    public fun emitAddTrack(track: WebRtcMedia.Track) {
        trackEventsFlow.tryEmit(TrackEvent.Add(track))
    }

    public fun emitRemoveTrack(track: WebRtcMedia.Track) {
        trackEventsFlow.tryEmit(TrackEvent.Remove(track))
    }

    public fun emitDataChannelEvent(event: DataChannelEvent) {
        dataChannelEventsFlow.tryEmit(event)
    }
}
